Перейти к основному содержимому

3.04. Текстовые данные

Разработчику Аналитику Тестировщику
Архитектору Инженеру

Текстовые данные

Текстовые данные — это последовательности символов, предназначенные для хранения, передачи и интерпретации информации в форме, читаемой человеком или обрабатываемой программами. В отличие от числовых, бинарных или мультимедийных данных, текст обладает особой структурной и семантической гибкостью: он может быть носителем естественного языка, программного кода, разметки, команд, метаданных и даже графических примитивов (например, эмодзи). Эта глава посвящена устройству текста как информационного объекта — от его физического представления в памяти до способов восприятия и интерпретации как людьми, так и программными системами.

Какими бывают текстовые данные

Текстовые данные классифицируются не по содержанию, а по структуре, назначению и уровню формализации. В повседневной практике программиста, системного администратора или технического писателя можно выделить следующие категории:

  • Простой (плоский) текст — последовательность символов без встроенных команд форматирования. Примеры: файлы .txt, исходные коды программ, логи приложений. Такой текст обрабатывается универсально, не требует специальных интерпретаторов и сохраняет совместимость между платформами при соблюдении кодировки.

  • Структурированный текст — содержит явные элементы организации: теги, разделители, иерархические блоки. К нему относятся XML, JSON, YAML, TOML, CSV (при строгом соблюдении формата). Такой текст допускает автоматизированное извлечение значений, валидацию схемы и трансформацию. Его структура задаётся синтаксическими правилами, а не только смыслом.

  • Полуструктурированный текст — сочетает свободную форму повествования с вкраплениями формализованных фрагментов. Примеры: электронные письма с заголовками в формате RFC 5322, документы Markdown, HTML-страницы с встроенным JavaScript или CSS. Здесь структура может быть локальной и не охватывать весь документ целиком.

  • Размеченный текст — несёт информацию не только о содержании, но и о его представлении или семантике. В отличие от простого текста, разметка содержит инструкции для движков отображения или обработки: например, <strong> в HTML указывает на семантическую важность фрагмента, а ** в Markdown — на визуальное выделение. Разметка может быть декларативной (как в LaTeX), императивной (как в RTF) или гибридной.

  • Исходный код — особый вид текста, предназначенный для интерпретации или компиляции в исполняемые инструкции. Хотя синтаксически он может напоминать структурированный или размеченный текст, его ключевое отличие — строгая грамматика и привязка к конкретной вычислительной модели. Ошибки в коде могут приводить не к искажению представления, а к сбою выполнения.

  • Мета- и служебные тексты — например, HTTP-заголовки, .gitignore, файлы локализации (.properties, .po), инструкции сборки (Makefile, Dockerfile). Такие тексты управляют поведением инструментов, но не предназначены для непосредственного чтения пользователем.

Важно понимать: границы между этими типами условны. Один и тот же текст может одновременно быть исходным кодом, структурированным документом и размеченным контентом — например, HTML-файл с встроенным <script> и <style>.

Как устроен текст

На самом низком уровне текст — это последовательность кодов, каждый из которых соответствует определённому графему (условной единице письменности). Графема может быть буквой, цифрой, знаком препинания, управляющим символом или составным элементом (например, буквой с диакритикой). Совокупность всех допустимых графем и их кодов образует кодировку символов.

Однако кодировка — не единственный уровень абстракции. Между человеком и машиной действует цепочка преобразований:

  1. Графема — абстрактное понятие (например, «латинская заглавная A»).
  2. Символьный код — целочисленное значение в рамках стандарта (например, U+0041 в Unicode).
  3. Кодовая единица — физическое представление кода в памяти (например, байт 0x41 в UTF-8 или два байта 0x00 0x41 в UTF-16BE).
  4. Глиф — визуальное воплощение символа, предоставляемое шрифтом (например, изображение «A» в шрифте Times New Roman).

Эта цепочка показывает, что текст — не монолитный объект, а результат согласованной работы нескольких подсистем: кодировочной таблицы, транскодера, шрифтового рендерера и интерпретатора разметки. Нарушение на любом этапе приводит к искажению: замене символа, отображению заглушки («□» или «») или полному сбою парсинга.

Строка и текст — в чём различие

В теории и практике программирования часто разделяют понятия строки и текста, хотя в бытовом употреблении они синонимичны.

  • Строка — это структура данных: упорядоченный неизменяемый (в большинстве языков) контейнер, хранящий последовательность кодовых единиц в определённой кодировке. Строка не несёт информации о языке, направлении письма, семантической роли слов. Для машины строка "123" идентична строке "abc" — обе представляют собой массив байтов или слов. Операции над строками (конкатенация, подстрока, поиск) производятся на уровне кодов, без учёта лингвистики.

  • Текст — это строка в контексте интерпретации. Текст предполагает наличие читателя — человека или алгоритма, способного извлечь смысл. Для текста важны: язык (для морфологического разбора), регистр (для восприятия тональности), пунктуация (для синтаксического анализа), направление (например, арабский или иврит пишутся справа налево), а также культурные конвенции (например, использование кавычек «ёлочкой» в русском vs „Gerade“ в немецком).

Таким образом, строка — это носитель, текст — содержание. Пример: строка "\u043F\u0440\u0438\u0432\u0435\u0442" в UTF-16 — это просто шесть 16-битных значений. Но в контексте русского языка она становится текстом «привет» — с определённым смыслом, интонацией приветствия и культурной маркировкой.

Структурированный и неструктурированный текст

Разделение на структурированный и неструктурированный текст — ключевое для проектирования систем хранения и обработки.

Неструктурированный текст — это последовательность символов без явных правил организации. К нему относятся художественные произведения, деловая переписка, записи в блогах, комментарии в коде. Такой текст требует применения методов обработки естественного языка (NLP): токенизации, именованного распознавания сущностей, классификации тональности. Его анализ трудоёмок и не всегда детерминирован.

Структурированный текст подчиняется строгой грамматике. Например, в JSON каждое значение должно быть одним из типов: строка в двойных кавычках, число, логическое значение, null, массив или объект. Наличие запятой в конце последнего элемента массива делает документ невалидным — система откажет в парсинге. Структурированность позволяет:

  • однозначно извлекать данные без двусмысленности;
  • строить схемы валидации (JSON Schema, XSD);
  • генерировать клиентский код по контракту (например, через OpenAPI);
  • осуществлять сериализацию/десериализацию с гарантией обратимости.

Между этими полюсами находится полуструктурированный текст. Он не требует полной формализации, но использует локальные шаблоны. Пример — лог-файл в формате:

[2025-11-20T12:34:56.789Z] INFO User login: timur@example.com

Здесь дата, уровень и событие выделены по позиции и разделителям. Такой текст можно парсить регулярными выражениями или специализированными парсерами, но небольшое изменение формата (например, добавление микросекунд) может нарушить обработку.

Важно: структурированность — не свойство текста как такового, а договорённость о формате между производителем и потребителем. Один и тот же физический файл может быть структурированным для одной системы (например, парсера логов) и неструктурированным для другой (например, поискового индексатора, который индексирует всё как сплошной поток токенов).


Кодировка

Кодировка (encoding) — это соглашение о том, каким образом символы естественного или формального языка отображаются в последовательность байтов для хранения или передачи. Кодировка определяет не только соответствие «символ ↔ число», но и способ кодирования этого числа в байты — особенно в случае многобайтовых систем.

ANSI и однобайтовые кодировки

Термин ANSI в контексте Windows исторически используется для обозначения однобайтовых кодовых страниц, таких как Windows-1251 (кириллица), Windows-1252 (западноевропейская латиница) или CP866 (DOS-кодировка). В этих системах один символ представлен ровно одним байтом, что даёт максимум 256 возможных значений. Однако 256 позиций недостаточно для одновременного представления нескольких алфавитов — поэтому возникали кодовые страницы: переключение набора символов в зависимости от локали. Например, байт 0xC0 в CP866 — это буква «А», в Windows-1251 — тоже «А», а в ISO-8859-1 — символ À. Отсутствие явного указания кодовой страницы в файлах приводило к широко известной проблеме «кракозябр»: открытие текста в неверной локали искажало содержимое.

Однобайтовые кодировки обладают рядом технических ограничений: невозможность смешивать языки (например, русский и греческий), отсутствие поддержки диакритики вне узкого региона, несовместимость между платформами. Несмотря на это, они сохраняют актуальность в legacy-системах, особенно в промышленных протоколах (Modbus ASCII, некоторые SCADA-интерфейсы), где экономия каждого байта критична.

Unicode и его кодировки: UTF-8, UTF-16, UTF-32

Unicode — не кодировка, а стандарт кодирования символов, определяющий уникальный числовой идентификатор (code point) для каждого графема. На момент 2025 года Unicode охватывает более 150 тысяч символов: алфавиты современных и древних языков, математические обозначения, символы игр, технические знаки, эмодзи и даже управляющие последовательности (например, для направления письма).

Сам по себе code point — абстрактное число, например U+0410 для «А» (кириллической), U+1F600 для «😀». Чтобы записать его в память, требуется конкретная кодировка. Основные варианты:

  • UTF-32 — каждый code point представлен 32-битным целым числом (4 байта). Это простейшая схема: индексация по символам тривиальна, длина строки в символах равна длине массива. Однако она неэффективна по памяти: латинские символы занимают 4 байта вместо 1. Применяется редко — в основном в внутренних буферах движков, где важна скорость доступа.

  • UTF-16 — использует 16-битные кодовые единицы. Символы из базовой многоязычной плоскости (BMP, U+0000–U+FFFF) кодируются одним юнитом. Символы выше (например, большинство эмодзи, U+10000–U+10FFFF) — парой суррогатных юнитов (high surrogate + low surrogate). Это приводит к тому, что длина строки в «юнитах» не совпадает с числом графем. Например, строка "👩‍💻" (женщина-разработчик) состоит из 7 code points, но в UTF-16 может занимать 11 юнитов. Именно поэтому методы вроде String.length в JavaScript или len() в Java (до Java 15) могут давать неожиданные результаты. UTF-16 — родная кодировка для Windows API, Java (внутренне) и .NET (в string), что обусловлено историческими причинами (первоначальная версия Unicode предполагала 16-битный охват).

  • UTF-8 — переменной длины, совместимая с ASCII. Символы в диапазоне U+0000–U+007F (латиница, цифры, пунктуация ASCII) кодируются одним байтом, идентичным их ASCII-коду. Это обеспечивает обратную совместимость: любой ASCII-файл автоматически является корректным UTF-8. Далее:

    • U+0080–U+07FF — 2 байта,
    • U+0800–U+FFFF — 3 байта (весь BMP, включая кириллицу, греческий, арабский),
    • U+10000–U+10FFFF — 4 байта (эмодзи, древние письменности).

UTF-8 стал де-факто стандартом для веба (более 98 % страниц по данным W3Techs), UNIX-систем, JSON, XML, протоколов (HTTP, SMTP) и большинства современных форматов. Её преимущества: компактность для латиницы, устойчивость к повреждениям (ошибка в одном байте не портит весь поток), отсутствие проблемы порядка байтов (endianness). Недостаток — сложность прямой индексации: чтобы перейти к n-му символу, нужно последовательно декодировать все предшествующие.

BOM и порядок байтов

В многобайтовых кодировках (особенно UTF-16 и UTF-32) возникает вопрос: в каком порядке хранить байты — старший сначала (big-endian, BE) или младший (little-endian, LE)? Чтобы избежать неоднозначности, может использоваться Byte Order Mark (BOM) — специальный невидимый символ U+FEFF в начале потока. Его кодировка указывает порядок: в UTF-16BE BOM — 0xFE 0xFF, в UTF-16LE — 0xFF 0xFE. В UTF-8 BOM (0xEF 0xBB 0xBF) не нужен, так как порядок байтов фиксирован, но он иногда добавляется Windows-редакторами для явной идентификации кодировки. Это может вызывать проблемы: например, PHP-скрипты с BOM в начале файла выдают «headers already sent», так как BOM интерпретируется как вывод.

Преобразование кодировок и совместимость

Преобразование текста из одной кодировки в другую — операция транскодирования. Она требует знания исходной кодировки; без этого данные неотличимы от произвольного бинарного шума. Большинство современных библиотек (ICU, iconv, .NET Encoding, Python str.encode()) поддерживают конвертацию через Unicode: исходная кодировка → Unicode code points → целевая кодировка. При этом возможна потеря информации: если целевая кодировка не содержит нужного символа, он заменяется заглушкой (например, ? или ``), либо возникает исключение.

Важный нюанс: объявление кодировки (например, <meta charset="utf-8"> в HTML или # -*- coding: utf-8 -*- в Python) не меняет физическое содержимое файла — оно лишь указывает интерпретатору, какой схемой декодировать байты. Если объявление не совпадает с фактической кодировкой, результат будет искажён. Для надёжного определения кодировки используются эвристики (анализ частотности байтов, проверка на валидность UTF-8) и сигнатуры (BOM, XML-декларация <?xml encoding="..."?>).


Шрифты

Шрифт — это набор графических описаний (глифов), сопоставленных символьным кодам. Шрифт не определяет значение символа — только его внешний вид. Один и тот же code point может отображаться по-разному в разных шрифтах: например, U+0061 (латинская a) в шрифте Courier выглядит моноширинной и угловатой, а в Comic Sans — округлой и неформальной.

Как работает шрифт

Современные шрифты (в основном форматов TrueType — .ttf и OpenType — .otf) содержат:

  • Таблицу cmap — сопоставление code point → идентификатор глифа.
  • Описание глифа — либо в виде векторных контуров (Bézier-кривые), либо в виде растровых битмапов (для малых размеров).
  • Метрики — кернинг (расстояние между конкретными парами символов), вынос (расстояние от базовой линии до верхней/нижней границы), интерлиньяж.
  • OpenType-функции — лигатуры (например, fi → «fi»), альтернативные формы, поддержка сложных сценариев (арабский, деванагари).

Рендеринг текста — многоэтапный процесс:

  1. Формирование строки — с учётом направления письма (LTR/RTL), объединения символов (например, буква + диакритика → один глиф), замены лигатур.
  2. Выбор шрифта — по стеку шрифтов: если текущий шрифт не содержит глифа для code point, система ищет его в резервных (fallback fonts).
  3. Глиф-рендеринг — векторные контуры растрируются в битмап с учётом размера, сглаживания (hinting, subpixel rendering), цвета.
  4. Композитинг — наложение глифов на фон, применение эффектов (тень, обводка).

В системах с ограниченными ресурсами (встроенные ОС, старые браузеры) используют растровые шрифты (.fon в Windows, bitmap fonts в Linux), где каждый размер хранится отдельно — это экономит CPU, но увеличивает объём.

Установка шрифтов

  • Windows: копирование .ttf/.otf в C:\Windows\Fonts (через Проводник или PowerShell Copy-Item). Шрифты становятся доступны всем приложениям после обновления кэша (fontcache).

  • Linux (десктоп): шрифты помещают в ~/.local/share/fonts/ (для пользователя) или /usr/share/fonts/ (глобально), затем вызывают fc-cache -fv для обновления кэша Fontconfig. Конфигурация через fonts.conf позволяет задавать приоритеты, алиасы, замены.

  • Android: системные шрифты — часть AOSP (/system/fonts). Приложения могут включать собственные шрифты в assets/fonts/ и подключать их через Typeface.createFromAsset().

  • Веб-разработка: шрифты подключаются через CSS-правило @font-face:

    @font-face {
    font-family: 'CustomFont';
    src: url('/fonts/custom.woff2') format('woff2'),
    url('/fonts/custom.woff') format('woff');
    font-display: swap; /* управляет поведением при загрузке */
    }

    Форматы WOFF/WOFF2 — сжатые обёртки над OTF/TTF, оптимизированные для сети. font-display определяет, показывать ли запасной шрифт во время загрузки (swap), скрывать текст (block) или использовать системный (fallback).

Важно: веб-шрифты подлежат CORS-проверке — сервер должен отправлять заголовок Access-Control-Allow-Origin. Кроме того, лицензии на шрифты часто ограничивают их встраивание в веб.


Таблицы символов, CHARMAP и «кракозябры»

Таблица символов в Windows: Character Map

Утилита charmap.exe предоставляет графический интерфейс к установленным шрифтам. Она позволяет:

  • Просматривать все глифы шрифта;
  • Копировать символы в буфер (включая недоступные на клавиатуре: §, ©, );
  • Видеть Unicode-код (например, U+2192 для →);
  • Выбирать подмножество (латиница, кириллица, математические символы).

CHARMAP не изменяет кодировку текста — он лишь помогает ввести нужный символ. Сам текст далее обрабатывается по тем же правилам, что и любой другой.

Природа «кракозябр» и иероглифов

«Кракозябры» — визуальный эффект неправильной интерпретации байтовой последовательности. Возникает, когда:

  1. Текст сохранён в кодировке A, но прочитан как кодировка B. Например, UTF-8-последовательность 0xD0 0x90 (кириллическая «А») интерпретирована как Windows-1252: 0xD0Ð, 0x90 → неразрывный пробел (отображается как `` или пропуск) — получаем Ð.

  2. Отсутствует шрифт с нужным глифом. Символ существует в кодировке, но рендерер не может его нарисовать — показывается placeholder (□, , [?]).

  3. Использованы управляющие символы. Например, U+202E (RIGHT-TO-LEFT OVERRIDE) может «перевернуть» отображение строки: "exe.txt ‮txt.exe"exe.txt txt.exe (вторая часть пишется справа налево, создавая иллюзию подмены расширения — метод фишинга).

«Иероглифы» в бытовом смысле — не обязательно китайские иероглифы, а любой текст, написанный алфавитом, незнакомым пользователю. С технической точки зрения, это корректный текст в соответствующей кодировке (чаще всего UTF-8), просто отсутствует лингвистическая компетенция для его интерпретации.


Эмодзи

Эмодзи — это символы Unicode, предназначенные для передачи эмоций, объектов и действий. Первые эмодзи были стандартизированы в Unicode 6.0 (2010), сейчас их более 3700.

Как работают эмодзи

Эмодзи — не изображения, а code points. Например:

  • U+1F600 — 😀 Grinning Face;
  • U+1F469 U+200D U+1F4BB — 👩‍💻 Woman Technologist (составной: женщина + zero-width joiner + компьютер).

Особенности:

  • Варианты отображения: многие эмодзи имеют текстовый (U+FE0E) и эмодзи-вариант (U+FE0F). Например, U+2764 U+FE0E → ❤ (чёрное сердце как символ), U+2764 U+FE0F → ❤️ (цветное сердце-эмодзи). Без варианта выбор зависит от шрифта и платформы.

  • Скин-тоны: эмодзи с людьми могут модифицироваться肤色 modifiers (U+1F3FB–U+1F3FF), добавляемыми после основного символа.

  • Составные эмодзи: используют zero-width joiner (U+200D) для объединения базовых символов (👨‍👩‍👧‍👦 = U+1F468 U+200D U+1F469 U+200D U+1F467 U+200D U+1F466).

Отрисовка эмодзи зависит от шрифта эмодзи, встроенного в ОС: Apple Color Emoji (macOS), Segoe UI Emoji (Windows), Noto Color Emoji (Android, Linux). Веб-браузеры используют системный шрифт по умолчанию.

Ввод эмодзи

  • Windows: Win + . (точка) или Win + ; вызывает панель эмодзи с поиском, историей, каракулями (sketch-to-emoji).
  • macOS: Cmd + Ctrl + Space.
  • Android/iOS: отдельная клавиша на экранных клавиатурах.
  • Linux: через ibus-emoji (ввод Ctrl+Shift+E, затем код или название).

Эмодзи допустимы в:

  • Именах файлов (но не рекомендуются — проблемы с legacy-системами);
  • URL (требуется percent-encoding, например, 😀%F0%9F%98%80);
  • JSON/XML (если кодировка UTF-8);
  • Исходном коде (в строковых литералах, комментариях; в идентификаторах — только в языках с поддержкой Unicode, например, Python 3, C#).

Регистр: не только вопрос вида

Регистр — свойство буквенного символа, указывающее на его форму: прописную (заглавную) или строчную. В Unicode регистр определяется не для всех символов — только для тех, где это лингвистически осмысленно (латиница, кириллица, греческий и др.). Для каждого символа с регистром заданы три операции:

  • toUpper — преобразование в верхний регистр,
  • toLower — в нижний,
  • toTitle — в заголовочный (первая буква слова — прописная, остальные — строчные, как в названиях глав).

Регистровые преобразования не всегда однозначны и зависят от языка. Например, в турецком языке латинская i в верхнем регистре — не I, а İ (I с точкой), а ı (i без точки) → I. Поэтому корректные библиотеки (например, ICU в Java, String.prototype.toLocaleUpperCase() в JavaScript) требуют указания локали: "i".toLocaleUpperCase('tr-TR')"İ".

Технически регистр реализован через таблицы case mapping в стандарте Unicode. Каждый code point имеет (если применимо):

  • Simple_Uppercase_Mapping,
  • Simple_Lowercase_Mapping,
  • Special_Casing (для контекстно-зависимых правил, например, немецкого ßSS в верхнем регистре).

Важно отличать визуальный регистр (начертание шрифта: small caps) от семантического (фактическое изменение code point). CSS-свойство text-transform: uppercase меняет отображение, но не содержимое DOM: document.querySelector('p').textContent вернёт исходный регистр. Для изменения данных требуется программное преобразование строки.


Раскладка клавиатуры

Раскладка клавиатуры — это таблица соответствия между физическими клавишами (и их модификаторами: Shift, AltGr) и генерируемыми символьными кодами. Раскладка не связана напрямую с кодировкой: одна и та же раскладка может использоваться в системах с разной кодовой страницей, а один и тот же символ может вводиться разными комбинациями в разных раскладках.

Стандартные раскладки:

  • QWERTY — латинская, доминирующая в англоязычных странах и де-факто глобально.
  • ЙЦУКЕН — кириллическая, принятая в России и странах СНГ.
  • AZERTY — французская, с переставленными клавишами и упрощённым доступом к диакритике.
  • Colemak/Dvorak — альтернативные эргономичные раскладки.

Механизм работы:

  1. При нажатии клавиши ОС получает скан-код — номер физической клавиши (например, 0x1E — клавиша «A» на PS/2).
  2. Драйвер клавиатуры, учитывая текущую раскладку и нажатые модификаторы, преобразует скан-код в виртуальный код (VK) и символьное сообщение (WM_CHAR в Windows, keyval в X11).
  3. Приложение получает Unicode code point (в современных системах) или код в локальной кодовой странице (в legacy-приложениях).

Смена раскладки может быть:

  • глобальной (на уровне ОС, действует для всех приложений),
  • прикладной (например, в редакторе кода: английская для кода, русская для комментариев),
  • контекстной (автоматическое переключение по языку ввода, как в Gboard).

В Linux управление раскладками осуществляется через XKB (X Keyboard Extension), в Windows — через Language Bar и API LoadKeyboardLayout. Для разработчиков важно: обработка ввода должна опираться на символьные события, а не на скан-коды — иначе приложение будет некорректно работать в не-QWERTY раскладках.


Пробелы и отступы: невидимая структура текста

Пробел — не единый символ, а класс whitespace-символов, каждый со своим назначением:

Code PointИмяНазначение
U+0020SPACEОбычный пробел — разделитель слов, сжимаемый в HTML.
U+00A0NO-BREAK SPACE (NBSP)Неразрывный пробел: предотвращает перенос («гл. 5», «100 руб.»).
U+2002EN SPACEШирина буквы «N» — используется в типографике.
U+2003EM SPACEШирина буквы «M» — для крупных отступов.
U+2009THIN SPACEУзкий пробел — между цифрой и единицей измерения (если разрешено ГОСТ).
U+202FNARROW NBSPУзкий неразрывный (например, в «10 000 руб»).
U+205FMEDIUM MATHEMATICAL SPACEВ формулах (редко используется).

Отступы (indentation) — средство структурирования текста. Различают:

  • Символьные отступы: табуляция (U+0009, U+000B, U+000C) или пробелы (U+0020). В исходных кодах предпочтение отдаётся пробелам (PEP 8, Google Style Guides), так как табуляция зависит от настроек редактора (ширина таба = 2, 4, 8 пробелов). Смешивание табов и пробелов в одном файле приводит к ошибкам (например, IndentationError в Python).

  • Семантические отступы: в Markdown и reStructuredText отступы задают вложенность списков или блоков кода. В HTML они не влияют на отображение (кроме <pre>), но улучшают читаемость разметки.

  • Типографские отступы: вёрстка использует CSS-свойства text-indent (красная строка), margin, padding. Эти отступы управляются не текстом, а стилями.

Особый случай — нулевая ширина: символы U+200B (ZERO WIDTH SPACE), U+200C (ZERO WIDTH NON-JOINER), U+200D (ZERO WIDTH JOINER). Они не занимают места, но влияют на поведение: разрешают перенос (U+200B), запрещают лигатуру (U+200C) или создают составные эмодзи (U+200D).


Синтаксис текста и разметка

Синтаксис — набор правил, определяющих допустимую структуру текстового формата. Синтаксис может быть:

  • Контекстно-свободным — описывается формальной грамматикой (например, BNF, EBNF). К нему относятся большинство языков программирования, JSON, XML.

  • Контекстно-зависимым — требует дополнительной информации для проверки (например, HTML: тег <li> допустим только внутри <ul> или <ol>; отступы в Python влияют на семантику).

  • Эвристическим — не имеет строгой спецификации, интерпретируется по принципу «лучшего соответствия». Примеры: логи, email-заголовки, некоторые конфигурационные файлы.

Разметка — способ внедрения синтаксических конструкций в текст для управления его представлением или семантикой. Основные парадигмы:

  1. Теговая (tag-based) — элементы окружены парными или самозакрывающимися тегами:
    <p>Абзац</p>, <br/>, <div id="main">.
    Примеры: HTML, XML. Требует балансировки тегов и экранирования (&lt; вместо <).

  2. Декларативная (markup by convention) — синтаксис основан на позиции и символах:
    # Заголовок, * Список, `код`.
    Примеры: Markdown, reStructuredText. Лёгок для чтения в исходнике, но сложен для парсинга (много двусмысленностей: *italic* vs * список).

  3. Императивная — команды указывают, как рендерить текст:
    {\bf Жирный}, \section{Глава}.
    Пример: LaTeX. Обладает точным контролем, но высоким порогом входа.

  4. Гибридная — сочетает подходы. Например, HTML-фрагменты внутри Markdown (допускаются, но не всегда рекомендуются).

Ключевой принцип современной разметки — семантическая разметка: теги должны описывать роль контента, а не его внешний вид. Например, <strong> (важно) предпочтительнее <b> (жирный), потому что первое сохраняет смысл при смене стилей или чтении скринридером.


Парсинг строк и текста

Парсинг — процесс анализа текста с целью извлечения структуры или значений. В зависимости от задачи и формата применяются разные техники:

  • Разделение по разделителюsplit(',') для CSV (устаревший, ненадёжный способ: не учитывает кавычки, переносы).

  • Регулярные выражения — мощный инструмент для извлечения паттернов (\d{4}-\d{2}-\d{2} для дат). Однако регулярки не справляются с вложенными структурами (например, HTML-тегами внутри атрибутов).

  • Конечные автоматы — используются в лексическом анализе (токенизации): сканер проходит по строке, меняя состояние в зависимости от символа («вне строки», «внутри строки», «в комментарии»).

  • Рекурсивный спуск (recursive descent) — ручная реализация парсера по грамматике. Подходит для простых DSL.

  • LL/LR-парсеры — генерируются из грамматики (ANTLR, Bison). Обеспечивают строгую проверку синтаксиса и построение AST (абстрактного синтаксического дерева).

  • DOM-парсеры — для XML/HTML: строят древовидную модель документа, поддерживают XPath, валидацию DTD/XSD.

Парсинг текста всегда начинается с токенизации — разбиения на лексемы (токены): идентификаторы, литералы, операторы, ключевые слова. Затем следует синтаксический анализ — построение структуры по правилам грамматики. Ошибки на любом этапе приводят к исключениям (SyntaxError, ParseException) или некорректному AST.

Важно: парсинг — не то же самое, что валидация. Парсер может успешно построить дерево из формально валидного, но семантически бессмысленного текста (например, int x = "text"; в C# — синтаксически корректно, но семантически ошибка).


Форматирование текста

Современные системы различают логическое (семантическое) форматирование и визуальное. Первое закладывается в разметку, второе — в стили или рендеринг.

Жирный, курсив, подчёркнутый, зачёркнутый

  • Жирный (<strong>, **текст**) — указывает на повышенную важность. В HTML по умолчанию рендерится как font-weight: bold, но стили можно переопределить.

  • Курсив (<em>, *текст*) — выделяет акцент, иностранные слова, термины. По умолчанию — font-style: italic.

  • Подчёркивание (<u>) — исторически означало гиперссылку. Сейчас используется редко (путает пользователей), но допустимо для орфографических ошибок (<u class="spelling-error">).

  • Зачёркивание (<s>, ~~текст~~) — устаревшая, недействительная или отменённая информация.

Все эти элементы — семантические теги, а не просто инструкции по отрисовке. Скринридеры озвучивают их особым образом («strong», «emphasized»), что критично для доступности.

Цитата

Цитата — выделение чужого высказывания. Различают:

  • Блок цитаты (<blockquote>) — для объёмных фрагментов. Может содержать атрибут cite с URL источника.
  • Строчная цитата (<q>) — для коротких фраз внутри абзаца. Браузеры автоматически добавляют кавычки в соответствии с локалью.

Моноширинный текст

Моноширинное начертание (<code>, `код`, <pre>) используется для:

  • фрагментов программного кода,
  • терминальных команд,
  • имён файлов и переменных,
  • данных в фиксированной ширине (логи, таблицы ASCII-art).

Ключевые особенности:

  • в <pre> сохраняются пробелы и переносы строк;
  • внутри <code> допустима вложенная разметка (например, <code>int <var>x</var> = 0;</code>).

Скрытый текст

Скрытый текст — содержимое, не отображаемое по умолчанию, но доступное программно или при определённых условиях. Реализуется через:

  • display: none (полное исключение из потока),
  • visibility: hidden (сохраняет место, но невидим),
  • aria-hidden="true" (скрытие от скринридеров, но видимость визуально).

Важно: скрытый текст не должен использоваться для манипуляций с поисковыми системами («cloaking»), что нарушает требования Google.

Гиперссылки

Гиперссылка (<a href="...">) — основной элемент гипертекста. Атрибуты:

  • href — целевой URI (относительный или абсолютный);
  • target="_blank" — открытие в новой вкладке (требует rel="noopener" для безопасности);
  • rel="nofollow" — указание поисковикам не передавать вес;
  • download — принудительная загрузка файла вместо навигации.

Для доступности:

  • текст ссылки должен быть осмысленным вне контекста (не «нажмите здесь»);
  • избегать ссылок из одного символа ([1]);
  • использовать aria-label, если визуальный текст неполон.